Effective Java 2nd Edition: 4-5章
第4章 クラスとインターフェース
項目13: class, member へのアクセス可能性を最小限にする
情報隠蔽 (カプセル化)
Javaのアクセス修飾子
Top level class/interface: public, package-private
Member: public, protected, package-private, private
ある1つのクラスでしか使われていないクラスはネストして private class にする
Serializable を実装したクラスはネストした private class でも公開APIの中に「漏れて」しまう (項目74, 75)
テストのために緩和するのは package-private までにしておく
インスタンスフィールドは public にするべきではない
public な可変フィールドを持つクラスはスレッドセーフではない
static フィールドでも同じことが言えるが、定数として public static final で公開するのはあり
長い
不変オブジェクトか primitive 型に限る
static final でもコレクションとかはクライアントから中身を変更できてしまう
項目14: public class では、 public のフィールドではなくアクセッサメソッドを使う
public フィールドだと不変式を強制できない、カプセル化の恩恵がない、表現形式の変更ができないなど問題がある
public class
フィールドは private, getter/setter を用意
final ならまあ大丈夫... かもしれない
package-private, nested private
利用範囲が限定されているため public フィールドでも大丈夫
項目15: 可変性を最小限にする
クラス、フィールドとも極力 final にする
コンストラクタ、アクセッサ、readObject() 内では防御的コピーをする
BigInteger, BigDecimal はサブクラス化可能なので安全ではない
防御的コピーをするなど「本当のBig何某」であるか検査しつつ使う
Serializable を実装してて可変オブジェクトを参照しているフィールドを持っている場合
readObject() か readResolve() を提供する
ObjectOutputStream.writeUnshared() と ObjectInputStream.readUnshared() を使う
項目16: 継承よりコンポジション
継承よりComposition
ここでいう継承 = 実装継承
継承だとスーパークラスの実装に依存してしまう、カプセル化を破る的な問題が
"B is-a A" の関係である (すべてのBはAである) 時のみ継承を行う
Wrapper クラスはコールバックフレームワークで使うには向いてない
SELF problem
項目17: 継承のために設計及び文書化する or 継承の禁止
override 可能なメソッドの自己利用について文書化する
コンストラクタが override 可能なメソッドを読んではいけない
継承のために設計されたクラスのテスト -> サブクラスを書く
項目18: abstract class より interface
abstract class は単一継承のみが許可されているため型定義としての用法はまずできない
「外部骨格実装クラス」を interface に対して提供すると abstract class と interface の長所を組み合わせられる
e.g. List と AbstractList ()
項目22: 非 static のメンバークラスより static メンバークラス
ネストされたクラスについて
Static member class
一般的には enclosing class と強調して機能する public ヘルパークラスとして使う
Non-static member class
Enclosing class のフィールドを参照できるが、対象のインスタンスとの関連付けが発生し、インスタンス生成時間が増加する
Enclosing class に関連する adapter として定義する (Map に対する KeySet とか)
Enclosing class のインスタンスにアクセスしないなら static を必ずつける
参照を持ってしまうのでGCで回収されない可能性
Anonymous class
使用される時点で宣言とインスタンス化が行われる (関数オブジェクトとか)
Local class
変数宣言ができるところならどこでも書ける
第5章 ジェネリクス
項目23: 新たなコードを原型で使用しない
Generic なクラス・インターフェイスをパラメータを使わずに使用しない
e.g. List<E> を List って使う
できるのは後方互換性のため
原型を使うべき時
class literal を使う時
instanceof する時 -> 検査後は非境界ワイルドカードで受け取って型安全性を確保しておく
項目24: 無検査警告を取り除く
型検査しないで代入・キャストをしない
明確に安全だと言える場合は @SuppressWarning("unchecked") を最低限のスコープで与えておく
項目25: 配列よりリスト
配列は covariant, List<E> は invariant
Sub extends Super な関係の場合 Sub[] は Super[] のサブタイプ
List<A> と List<B> は A と B が継承関係にあっても型の上下関係が発生しない
配列の型ミスマッチは実行じ、リストのミスマッチはコンパイル時に検出
Erasure
項目27: ジェネリックメソッドを使用する
相互比較可能にする型の表現: <T extends Comparable<T>> (再帰型境界)
Generic static factory パターンは現在では考慮しなくてよさそう
項目28: API の柔軟性向上のために境界ワイルドカードを使用する
パラメータ化された型は invariant
対象の型以外はサブタイプでも適用できない
でもサブタイプまで適用できた方が便利なケースもある
<? extends T>: T とそのサブタイプが適用範囲になる
<? super T>: T とそのスーパータイプが適用範囲になる
戻り値型にワイルドカードを使用しない
項目27の相互比較可能な型の表現にワイルドカードを適用する
<T extends Comparable<? super T>>
Comaprable だけでなく Comparator も Comparator<? super T> と書く
項目29: 型安全な異種コンテナを検討する
クラス自体は generic じゃないけどメソッドが generic
code:java
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();
public <T> void putFavorite(Class<T> type, T instance) {
if (type == null)
throw new NullPointerException();
favorites.put(type, type.cast(instance));
}
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
}
}
#Java